<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #tooltip {
            color: white;
            opacity: .9;
            background: #333;
            padding: 5px;
            border: 1px solid lightgrey;
            border-radius: 5px;
            position: absolute;
            z-index: 10;
            visibility: hidden;
            white-space: nowrap;
            pointer-events: none;
        }

        #circle circle {
            fill: none;
            pointer-events: all;
        }

        path.group {
            fill-opacity: .8;
        }

        path.chord {
            fill-opacity: .8;
            stroke: #000;
            stroke-width: .25px;
        }

        #circle:hover path.fade {
            display: none;
        }
    </style>
    <script src="lib/d3.v7.min.js"></script>
</head>
<body>
<div id="tooltip"></div>
<svg id="chordDiagram"></svg>
<script>
    // 数据示例，表示4个节点之间的连接关系
    const data = [
        {currentJob: "A", desiredJob: "B", count: 10},
        {currentJob: "A", desiredJob: "C", count: 5},
        {currentJob: "A", desiredJob: "D", count: 7},
        {currentJob: "B", desiredJob: "A", count: 8},
        {currentJob: "B", desiredJob: "C", count: 3},
        {currentJob: "C", desiredJob: "A", count: 2},
        {currentJob: "C", desiredJob: "B", count: 4},
        {currentJob: "D", desiredJob: "B", count: 3},
        {currentJob: "D", desiredJob: "A", count: 3}
    ];

    // 创建节点列表
    const nodes = Array.from(new Set(data.map(d => d.currentJob).concat(data.map(d => d.desiredJob))));

    // 创建空的矩阵
    const matrix = new Array(nodes.length).fill(0).map(() => new Array(nodes.length).fill(0));

    // 填充矩阵
    data.forEach(d => {
        const i = nodes.indexOf(d.currentJob);
        const j = nodes.indexOf(d.desiredJob);
        matrix[i][j] = d.count;
    });

    // 图表参数
    const width = 900;
    const height = 900;
    const outerRadius = Math.min(width, height) * 0.5 - 40;
    const innerRadius = outerRadius - 30;

    // 创建弦合图布局
    const chord = d3.chord()
        .padAngle(0.05)
        .sortSubgroups(d3.descending);

    // 创建弦合图生成器
    const arc = d3.arc()
        .innerRadius(innerRadius)
        .outerRadius(outerRadius);

    const ribbon = d3.ribbon()
        .radius(innerRadius);

    // 创建SVG容器
    const svg = d3.select("#chordDiagram")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    // 生成布局数据
    const chords = chord(matrix);

    // 绘制节点弧
    const group = svg.append("g")
        .selectAll("g")
        .data(chords.groups)
        .enter().append("g");

    group.append("path")
        .attr("fill", "steelblue")
        .attr("d", arc);

    // 添加节点标签
    group.append("text")
        .each(function (d) {
            d.angle = (d.startAngle + d.endAngle) / 2;
        })
        .attr("dy", ".35em")
        .attr("transform", function (d) {
            return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
                + "translate(" + (outerRadius + 10) + ")"
                + (d.angle > Math.PI ? "rotate(180)" : "");
        })
        .attr("text-anchor", function (d) {
            return d.angle > Math.PI ? "end" : null;
        })
        .text(function (d, i) {
            return nodes[i];
        });

    // 绘制弦
    svg.append("g")
        .attr("fill-opacity", 0.7)
        .selectAll("path")
        .data(chords)
        .enter().append("path")
        .attr("d", ribbon)
        .attr("fill", "steelblue")
        .on("mouseover", showTooltip)
        .on("mouseout", hideTooltip);

    // 添加信息提示框元素
    const tooltip = d3.select("body")
        .append("div")
        .attr("class", "tooltip")
        .style("opacity", 0);

    // 定义mouseover事件处理函数
    function showTooltip(d) {
        tooltip.transition()
            .duration(200)
            .style("opacity", .9);
        tooltip.html("关系数量：" + d.source.value)
            .style("left", (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
    }

    function mouseover(d, i) {
        d3.select("#tooltip")
            .style("visibility", "visible")
            .html(groupTip(rdr(d)))
            .style("top", function () {
                return (d3.event.pageY - 80) + "px"
            })
            .style("left", function () {
                return (d3.event.pageX - 130) + "px";
            })

        chordPaths.classed("fade", function (p) {
            return p.source.index !== i
                && p.target.index !== i;
        });
    }

</script>
</body>
</html>